Usando dados do INMEP para observação do clime no Brasil em 2022¶

image.png image-2.png

Este notebook visa usar vários aprendizados em Python e suas bibliotecas, para análise dos dados climáticos disponíveis em https://bdmep.inmet.gov.br/, incluindo análise visual por mapas

Um processamento básico foi feito sobre os dados originais (que são médias diárias registradas por várias estações em funcionamento no Brasil). Este processamento foi salvo em arquivos separados:

- inmet_2022_daily_observations.csv
- inmet_2022_stations

Ao longo do exercício, alguns artigos jornalísticos relatando eventos anormais são referenciados, para tentar achar nos dados, as indicações reportadas por estes artigos.

In [1]:
import pandas as pd
import folium

Carga dos arquivos¶

Os arquivos disponíveis são registros obtidos por 567 estações espalhadas pelo Brasil, um arquivo por estação. Cada registro destes arquivos correspondem à média horária de várias variáveis climáticas, todas detalhando Pressão, Temperatura, Humidade, Ventos, Precipitação e Radiação.

Eu decidi trabalhar com DataFrames em separado: Medições e Estações usando como chave o código da estação, quando necessário. Porém, sugiro mantê-los unificados, para menor esforço.

Na carga, eu agrupei-os por dia, usando as médias de cada variável, e salvando nos arquivos processados, se necessário.

Um dos problemas destes arquivos é que eles não contém medições em todos os dias de 2022. Para simplificação, eu os desconsiderei, quando possível.

In [2]:
numeric_features=["PRECIPITAÇÃO TOTAL, HORÁRIO (mm)",
                  "PRESSAO ATMOSFERICA AO NIVEL DA ESTACAO, HORARIA (mB)",
                  "PRESSÃO ATMOSFERICA MAX.NA HORA ANT. (AUT) (mB)",
                  "PRESSÃO ATMOSFERICA MIN. NA HORA ANT. (AUT) (mB)",
                  "RADIACAO GLOBAL (Kj/m²)",
                  "TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)",
                  "TEMPERATURA DO PONTO DE ORVALHO (°C)",
                  "TEMPERATURA MÁXIMA NA HORA ANT. (AUT) (°C)",
                  "TEMPERATURA MÍNIMA NA HORA ANT. (AUT) (°C)",
                  "TEMPERATURA ORVALHO MIN. NA HORA ANT. (AUT) (°C)",
                  "UMIDADE REL. MAX. NA HORA ANT. (AUT) (%)",
                  "UMIDADE REL. MIN. NA HORA ANT. (AUT) (%)",
                  "UMIDADE RELATIVA DO AR, HORARIA (%)",
                  "VENTO, DIREÇÃO HORARIA (gr) (° (gr))",
                  "VENTO, RAJADA MAXIMA (m/s)",
                  "VENTO, VELOCIDADE HORARIA (m/s)"
]

wheather_mean_parameters = {
    "Precipitação":"PRECIPITAÇÃO TOTAL, HORÁRIO (mm)_mean",
    "Umidade":"UMIDADE RELATIVA DO AR, HORARIA (%)_mean",
    "Pressão":"PRESSAO ATMOSFERICA AO NIVEL DA ESTACAO, HORARIA (mB)_mean",
    "Temperatura":"TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean",
    "Vento":"VENTO, VELOCIDADE HORARIA (m/s)_mean",
    "Radiação":"RADIACAO GLOBAL (Kj/m²)_mean"
}

wheather_max_parameters = {
    "Precipitação":"PRECIPITAÇÃO TOTAL, HORÁRIO (mm)",
    "Umidade":"UMIDADE REL. MAX. NA HORA ANT. (AUT) (%)",
    "Pressão":"PRESSÃO ATMOSFERICA MAX.NA HORA ANT. (AUT) (mB)",
    "Temperatura":"TEMPERATURA MÁXIMA NA HORA ANT. (AUT) (°C)",
    "Vento":"VENTO, RAJADA MAXIMA (m/s)",
    "Radiação":"RADIACAO GLOBAL (Kj/m²)"
}

TEMP_INDICATORS = {
    "mean":
        [
            "TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean",
            "TEMPERATURA DO PONTO DE ORVALHO (°C)_mean",
            "TEMPERATURA MÁXIMA NA HORA ANT. (AUT) (°C)_mean",
            "TEMPERATURA MÍNIMA NA HORA ANT. (AUT) (°C)_mean",
            "TEMPERATURA ORVALHO MIN. NA HORA ANT. (AUT) (°C)_mean"
        ],
    "min":
        [
            "TEMPERATURA MÍNIMA NA HORA ANT. (AUT) (°C)_min",
            "TEMPERATURA ORVALHO MIN. NA HORA ANT. (AUT) (°C)_min"
        ],
    "max":
        [
            "TEMPERATURA MÁXIMA NA HORA ANT. (AUT) (°C)_max",
            "TEMPERATURA ORVALHO MIN. NA HORA ANT. (AUT) (°C)_max"
        ]   
}

PRESSURE_INDICATORS = [
    "PRESSAO ATMOSFERICA AO NIVEL DA ESTACAO, HORARIA (mB)",
    "PRESSÃO ATMOSFERICA MAX.NA HORA ANT. (AUT) (mB)",
    "PRESSÃO ATMOSFERICA MIN. NA HORA ANT. (AUT) (mB)",
]

RADIATION_INDICATORS = ["RADIACAO GLOBAL (Kj/m²)"]

HUMIDITY_INDICATORS = [
    "UMIDADE REL. MAX. NA HORA ANT. (AUT) (%)",
    "UMIDADE REL. MAX. NA HORA ANT. (AUT) (%)",
    "UMIDADE REL. MIN. NA HORA ANT. (AUT) (%)",
    "UMIDADE RELATIVA DO AR, HORARIA (%)"
]

WIND_INDICATORS = [
    "VENTO, DIREÇÃO HORARIA (gr) (° (gr))",
    "VENTO, RAJADA MAXIMA (m/s)",
    "VENTO, VELOCIDADE HORARIA (m/s)"
]
In [3]:
data_dir = "../data/INMET_2022/"
inmet_encoding='ISO-8859-1'
inmet_delimiter=";"
In [4]:
def load_files(filename):
    full_name =  data_dir + filename
    #print (f"loading {filename} ...")
    
    station_df = pd.read_csv(
        full_name,
        encoding=inmet_encoding,
        delimiter=inmet_delimiter,
        decimal=",",
        skiprows=8)
    
    # Observations
    station_daily_df = station_df.groupby(by="Data")[numeric_features].agg(['mean','min','max'])
    
    # Station parameters
    idx=1
    params = []
    with open(full_name) as file:
        for line in file:
            line = line.rstrip("\n")
            param = line.split(":;")
            params.append(param)
            idx+=1
            if idx == 9:
                break

    station_daily_df["CODIGO (WMO)"] = params[3][1]
    
    #station_params_df = pd.DataFrame(columns=['REGIAO','UF','ESTACAO','CODIGO (WMO)','LATITUDE','LONGITUDE','ALTITUDE','DATA DE FUNDACAO'])
    
    param_row={
        params[0][0]:params[0][1],
        params[1][0]:params[1][1],
        params[2][0]:params[2][1],
        params[3][0]:params[3][1],
        params[4][0]:float(params[4][1].replace(",",".")),
        params[5][0]:float(params[5][1].replace(",",".")),
        params[6][0]:float(params[6][1].replace(",",".")),
        params[7][0]:params[7][1],
    }

    station_params_df = pd.DataFrame([param_row])
    #station_params_df = pd.concat([station_params_df,df_dictionary])

    return station_daily_df,station_params_df
In [5]:
# Skip this cell, if it was done already and you decide to use the processed files:
#    inmet_2022_daily_observations.csv
#    inmet_2022_stations.csv

import os
station_obs = pd.DataFrame()
station_params = pd.DataFrame()

files = os.listdir(data_dir)
for file in files:
    obs_df,params_df = load_files(file)
    station_obs = pd.concat([station_obs,obs_df])
    station_params = pd.concat([station_params,params_df])

new_columns = [col[0] + "_" + col[1] if col[1] in ['mean','min','max'] else col[0] for col in station_obs.columns]
station_obs.columns = new_columns
station_obs.dropna(inplace=True)
station_obs.to_csv('../data/inmet_2022_daily_observations.csv')
station_params.to_csv('../data/inmet_2022_stations.csv', index=False)
In [6]:
station_obs=pd.read_csv('../data/inmet_2022_daily_observations.csv',index_col="Data")
station_params = pd.read_csv('../data/inmet_2022_stations.csv')

Mean Temperatures¶

Lowest mean temperatures¶

In [7]:
temp_avg = station_obs.sort_values("TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean",ascending=True)[TEMP_INDICATORS['mean'] + ["CODIGO (WMO)"]]
temp_avg = pd.merge(temp_avg,station_params,how='left',on="CODIGO (WMO)")    
temp_avg.head(3)
Out[7]:
TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean TEMPERATURA DO PONTO DE ORVALHO (°C)_mean TEMPERATURA MÁXIMA NA HORA ANT. (AUT) (°C)_mean TEMPERATURA MÍNIMA NA HORA ANT. (AUT) (°C)_mean TEMPERATURA ORVALHO MIN. NA HORA ANT. (AUT) (°C)_mean CODIGO (WMO) REGIAO UF ESTACAO LATITUDE LONGITUDE ALTITUDE DATA DE FUNDACAO
0 0.670833 -3.683333 1.166667 0.200000 -4.216667 A815 S SC SAO JOAQUIM -28.27564 -49.934617 1400.06 12/04/08
1 1.100000 -3.654167 1.825000 0.612500 -4.329167 A815 S SC SAO JOAQUIM -28.27564 -49.934617 1400.06 12/04/08
2 1.412500 -4.037500 1.912500 0.820833 -4.641667 A815 S SC SAO JOAQUIM -28.27564 -49.934617 1400.06 12/04/08

Highest mean temperatures¶

In [8]:
temp_avg.tail(3)
Out[8]:
TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean TEMPERATURA DO PONTO DE ORVALHO (°C)_mean TEMPERATURA MÁXIMA NA HORA ANT. (AUT) (°C)_mean TEMPERATURA MÍNIMA NA HORA ANT. (AUT) (°C)_mean TEMPERATURA ORVALHO MIN. NA HORA ANT. (AUT) (°C)_mean CODIGO (WMO) REGIAO UF ESTACAO LATITUDE LONGITUDE ALTITUDE DATA DE FUNDACAO
123022 36.433333 11.400000 37.400000 36.100000 11.150000 A331 NE PI SAO JOAO DO PIAUI -8.364444 -42.252500 231.00 26/08/07
123023 36.700000 17.255556 37.800000 35.077778 16.488889 A869 S PR CIDADE GAUCHA -23.359167 -52.931944 365.79 11/03/08
123024 36.800000 10.750000 38.133333 36.100000 8.666667 A331 NE PI SAO JOAO DO PIAUI -8.364444 -42.252500 231.00 26/08/07

Min temperatures¶

In [9]:
temp_min = station_obs.groupby("CODIGO (WMO)").agg("min")[TEMP_INDICATORS['min']]
temp_min = temp_min.sort_values("TEMPERATURA MÍNIMA NA HORA ANT. (AUT) (°C)_min",ascending=True).head(10)
temp_min = pd.merge(temp_min,station_params,how='left',on="CODIGO (WMO)")    
temp_min
Out[9]:
CODIGO (WMO) TEMPERATURA MÍNIMA NA HORA ANT. (AUT) (°C)_min TEMPERATURA ORVALHO MIN. NA HORA ANT. (AUT) (°C)_min REGIAO UF ESTACAO LATITUDE LONGITUDE ALTITUDE DATA DE FUNDACAO
0 A875 -3.9 -8.8 S PR GENERAL CARNEIRO -26.398611 -51.353611 1009.01 30/04/08
1 A815 -3.9 -19.7 S SC SAO JOAQUIM -28.275640 -49.934617 1400.06 12/04/08
2 A880 -2.7 -5.8 S RS VACARIA -28.513602 -50.882738 969.89 26/04/08
3 A831 -2.5 -5.7 S RS QUARAI -30.368578 -56.437115 113.05 17/10/07
4 A874 -2.3 -9.8 S PR SAO MATEUS DO SUL -25.835556 -50.368889 780.21 17/04/11
5 A894 -2.2 -6.5 S RS SERAFINA CORREA -28.704722 -51.870833 545.00 01/04/16
6 A897 -2.1 -7.9 S RS CAMBARA DO SUL -29.049167 -50.149722 1017.00 24/11/16
7 A828 -1.7 -4.0 S RS ERECHIM -27.657778 -52.305833 777.08 25/11/06
8 A879 -1.6 -6.9 S RS CANELA -29.368889 -50.827222 830.93 23/08/08
9 A898 -1.5 -4.8 S SC CAMPOS NOVOS -27.388611 -51.215833 963.00 15/02/19

Max temperatures¶

In [10]:
temp_max = station_obs.groupby("CODIGO (WMO)").agg("max")[TEMP_INDICATORS['max']]
temp_max = temp_max.sort_values("TEMPERATURA MÁXIMA NA HORA ANT. (AUT) (°C)_max",ascending=False).head(10)
temp_max = pd.merge(temp_max,station_params,how='left',on="CODIGO (WMO)")    
temp_max
Out[10]:
CODIGO (WMO) TEMPERATURA MÁXIMA NA HORA ANT. (AUT) (°C)_max TEMPERATURA ORVALHO MIN. NA HORA ANT. (AUT) (°C)_max REGIAO UF ESTACAO LATITUDE LONGITUDE ALTITUDE DATA DE FUNDACAO
0 A809 42.9 24.4 S RS URUGUAIANA -29.839870 -57.081899 74.29 28/09/06
1 A830 42.2 23.8 S RS SAO BORJA -28.650000 -56.016389 81.08 21/07/07
2 A852 42.0 23.5 S RS SAO LUIZ GONZAGA -28.417222 -54.962500 245.50 25/07/07
3 A882 41.7 23.8 S RS TEUTONIA -29.449167 -51.823333 81.00 04/10/12
4 A831 41.5 23.0 S RS QUARAI -30.368578 -56.437115 113.05 17/10/07
5 A724 41.4 23.6 CO MS CORUMBA -18.996667 -57.637500 111.73 26/10/06
6 A354 41.1 24.1 NE PI OEIRAS -6.974167 -42.146944 154.03 22/07/08
7 A326 41.1 24.7 NE PI BOM JESUS DO PIAUI -9.083333 -44.326389 296.00 17/07/07
8 A826 40.9 24.1 S RS ALEGRETE -29.709167 -55.525556 120.88 28/09/06
9 A833 40.9 22.4 S RS SANTIAGO -29.191599 -54.885653 390.03 04/02/09

Inference¶

Where are the stations?¶

In [11]:
import leafmap.leafmap as leafmap
Map = leafmap.Map(center=[-15, -48], zoom=4)
#Map.add_xy_data(station_params,  x="LONGITUDE", y="LATITUDE", layer_name="INMET Stations")
Map.add_points_from_xy(
    station_params,
    x="LONGITUDE",
    y="LATITUDE",
    #color_column='region',
    icon_names=['gear', 'map', 'leaf', 'globe'],
    spin=True,
    add_legend=True,
)
Map
Map(center=[-15, -48], controls=(ZoomControl(options=['position', 'zoom_in_text', 'zoom_in_title', 'zoom_out_t…

National Metrics¶

Temperature¶

Average temperature registered by each station¶

In [12]:
year_avg_temp = station_obs.groupby("CODIGO (WMO)")[[("TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean")]].mean()
year_avg_temp = pd.merge(year_avg_temp, station_params, how='left', on='CODIGO (WMO)')
year_avg_temp = year_avg_temp[year_avg_temp["TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean"].notna()]
In [13]:
m = folium.Map([-15, -48], zoom_start=4)

for i in range(0,len(year_avg_temp)):

    if (year_avg_temp.iloc[i]['TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean']) <= 15:
        station_color =  "green"
    elif year_avg_temp.iloc[i]['TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean'] <= 20:
        station_color =  "blue"
    elif year_avg_temp.iloc[i]['TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean'] <= 27:
        station_color =  "gray"        
    elif year_avg_temp.iloc[i]['TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean'] <= 30:
        station_color =  "red"
    else:
        station_color =  "black"
    
    folium.Marker(
        location=[year_avg_temp.iloc[i]['LATITUDE'], year_avg_temp.iloc[i]['LONGITUDE']],
        popup=round(year_avg_temp.iloc[i]['TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean'],1),
        #icon=folium.DivIcon(html=f"""<div style="font-family: courier new; color: {station_color}">{year_avg_temp.iloc[i]['temp_str']}</div>""")
        icon=folium.Icon(color=station_color),
    ).add_to(m)
    
m
Out[13]:
Make this Notebook Trusted to load map: File -> Trust Notebook

State Metrics¶

Temperature¶

Average temperature by state, with top 10 highest and lowest registered¶

In [14]:
import geopandas as gpd
import os
CONDA_PREFIX = r"C:\Users\hcord\anaconda3\envs\geo"
os.environ['GDAL_DATA'] = os.environ['CONDA_PREFIX'] + r'\Library\share\gdal'
In [15]:
code_df = station_obs.copy()
In [16]:
code_df = pd.merge(code_df, station_params, how='left', on='CODIGO (WMO)')
In [17]:
uf_temp_avg = code_df.groupby("UF")[["TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean"]].agg("mean").reset_index()
In [18]:
m_states = folium.Map(location=(-15, -48), zoom_start=4, tiles="cartodb positron")

folium.Choropleth(
    geo_data="../data/Brazil.json",
    data=code_df,
    columns=["UF", "TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean"],
    key_on="feature.properties.UF",
    fill_color="RdYlGn_r",
    fill_opacity=0.8,
    line_opacity=0.3,
    nan_fill_color="white",
).add_to(m_states)

# Mark the top 10 locations of highest temps
for index, row in temp_max.iterrows():    
    folium.Marker(
        location=[row['LATITUDE'], row['LONGITUDE']],
        popup=row['TEMPERATURA MÁXIMA NA HORA ANT. (AUT) (°C)_max'],
        icon=folium.Icon(color="red"),
    ).add_to(m_states)

# Mark the top 10 locations of lowest temps
for index, row in temp_min.iterrows():    
    folium.Marker(
        location=[row['LATITUDE'], row['LONGITUDE']],
        popup=row['TEMPERATURA MÍNIMA NA HORA ANT. (AUT) (°C)_min'],
        icon=folium.Icon(color="green"),
    ).add_to(m_states)

m_states
Out[18]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Remember that these highest and lowest occurr in different times. Eventhough, it is surprising the the state of Rio Grande do Sul present most of both metrics

Unusual Scenarios¶

In 2022 Brazil has experienced unusual/extreme meteorological scenarios. According to https://www.tempo.com/noticias/actualidade/retrospectiva-2022-relembre-os-eventos-climaticos-que-marcaram-o-ano-eventos-extremos-tempo-severo-onda-de-frio.html, these are the most relevant:

  1. Severe rains / precipitations in Nortn, Northeast, Mid-West and mainly in Southeast regions.
  2. Extreme low temperatures in Parque Nacional do Itatiaia in the begining of 2022 winter (Southeast region)
In [19]:
station_full = pd.merge(station_obs, station_params, how='inner', on='CODIGO (WMO)')

Precipitations¶

State¶

In [20]:
# Total per year
#total_anual_precip_per_state = pd.merge(station_obs, station_params, how='inner', on='CODIGO (WMO)')
states_highest_precip = station_full.groupby("UF")[["PRECIPITAÇÃO TOTAL, HORÁRIO (mm)_mean"]].mean().sort_values("PRECIPITAÇÃO TOTAL, HORÁRIO (mm)_mean",ascending=False)
states_highest_precip.head(4)
Out[20]:
PRECIPITAÇÃO TOTAL, HORÁRIO (mm)_mean
UF
AC 0.281539
AP 0.279640
AM 0.249090
PA 0.237956

Stations¶

In [21]:
# Total per year - Average
stations_highest_precip = station_full.loc[(station_full["UF"].isin(["PA","AC","RJ","SC"]))]
stations_highest_precip = stations_highest_precip.groupby("ESTACAO")[["PRECIPITAÇÃO TOTAL, HORÁRIO (mm)_mean"]].mean().sort_values("PRECIPITAÇÃO TOTAL, HORÁRIO (mm)_mean",ascending=False)
stations_highest_precip.head(10)
Out[21]:
PRECIPITAÇÃO TOTAL, HORÁRIO (mm)_mean
ESTACAO
BREVES 0.700238
BOM JARDIM DA SERRA - MORRO DA IGREJA 0.699762
PARAGOMINAS 0.561582
CAPITAO POCO 0.533280
SERRA DOS CARAJAS 0.471385
BELEM 0.407782
TERESOPOLIS-PARQUE NACIONAL 0.369740
RIO BRANCO 0.345753
NOVO HORIZONTE 0.342969
BRAGANCA 0.326365
In [22]:
# Total per year - Sum
stations_highest_precip = station_full.loc[(station_full["UF"].isin(["PA","AC","RJ","SC"]))]
stations_highest_precip = stations_highest_precip.groupby("ESTACAO")[["PRECIPITAÇÃO TOTAL, HORÁRIO (mm)_mean"]].sum().sort_values("PRECIPITAÇÃO TOTAL, HORÁRIO (mm)_mean",ascending=False)
pd.merge(stations_highest_precip.reset_index(),station_params,how="left",on="ESTACAO").sort_values("PRECIPITAÇÃO TOTAL, HORÁRIO (mm)_mean",ascending=False)[["ESTACAO","PRECIPITAÇÃO TOTAL, HORÁRIO (mm)_mean","REGIAO","UF"]].head(10)
Out[22]:
ESTACAO PRECIPITAÇÃO TOTAL, HORÁRIO (mm)_mean REGIAO UF
0 TERESOPOLIS-PARQUE NACIONAL 132.366785 SE RJ
1 ANGRA DOS REIS 115.022609 SE RJ
2 BELEM 114.994438 N PA
3 ITAPOA 108.107971 S SC
4 DIONISIO CERQUEIRA 100.526394 S SC
5 JOACABA 97.184139 S SC
6 CASTANHAL 97.018615 N PA
7 CAMPOS NOVOS 96.465048 S SC
8 PARQUE ESTADUAL CHANDLESS 96.083246 N AC
9 NOVA FRIBURGO - SALINAS 95.283333 SE RJ

Humidity¶

State¶

In [23]:
# Total per year
#avg_hum_per_state = pd.merge(station_obs, station_params, how='inner', on='CODIGO (WMO)')
states_avg_hum = station_full.groupby("UF")[["UMIDADE RELATIVA DO AR, HORARIA (%)_mean"]].mean().sort_values("UMIDADE RELATIVA DO AR, HORARIA (%)_mean",ascending=True)
states_avg_hum.head(4)
Out[23]:
UMIDADE RELATIVA DO AR, HORARIA (%)_mean
UF
RO 47.355560
SE 58.566048
PI 60.550638
RN 61.668510

Stations¶

In [24]:
# Total per year
stations_lowest_hum = station_full.loc[(station_full["UF"].isin(["GO","DF","MS","SP"]))]
stations_lowest_hum = stations_lowest_hum.groupby("ESTACAO")[["UMIDADE RELATIVA DO AR, HORARIA (%)_mean"]].mean().sort_values("UMIDADE RELATIVA DO AR, HORARIA (%)_mean",ascending=True)
pd.merge(stations_lowest_hum.reset_index(),station_params,how="left",on="ESTACAO").sort_values("UMIDADE RELATIVA DO AR, HORARIA (%)_mean",ascending=True)[["ESTACAO","UMIDADE RELATIVA DO AR, HORARIA (%)_mean","REGIAO","UF"]].head(10)
Out[24]:
ESTACAO UMIDADE RELATIVA DO AR, HORARIA (%)_mean REGIAO UF
0 CASSILANDIA 47.430481 CO MS
1 POSSE 48.993281 CO GO
2 CORUMBA 52.650113 CO MS
3 PARANAIBA 55.431436 CO MS
4 JALES 56.516019 SE SP
5 ARIRANHA 57.039319 SE SP
6 GOIANESIA 57.802712 CO GO
7 GOIANIA 58.564266 CO GO
8 CATALAO 58.678764 CO GO
9 MONTE ALEGRE DE GOIAS 58.823740 CO GO

Temperatures¶

State¶

In [25]:
# Total per year
#temps_per_state = pd.merge(station_obs, station_params, how='inner', on='CODIGO (WMO)')
temps_per_state = station_full.groupby("UF")[["TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean"]].mean().sort_values("TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean",ascending=False)
temps_per_state.head(4)
Out[25]:
TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean
UF
RO 30.050422
RN 28.662405
PI 27.728511
MA 27.249262

Stations¶

In [26]:
# Total per year
stations_high_temps = station_full.loc[(station_full["UF"].isin(["PA","AM","AC","MT"]))]
stations_high_temps = stations_high_temps.groupby("ESTACAO")[["TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean"]].mean().sort_values("TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean",ascending=False)
pd.merge(stations_high_temps.reset_index(),station_params,how="left",on="ESTACAO").sort_values("TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean",ascending=False)[["ESTACAO","TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean","REGIAO","UF"]].head(10)
Out[26]:
ESTACAO TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean REGIAO UF
0 XINGUARA 29.125891 N PA
1 SAO FELIX DO ARAGUAIA 28.151389 CO MT
2 CASTANHAL 27.941016 N PA
3 TUCURUI 27.772724 N PA
4 ITAITUBA 27.615391 N PA
5 CUIABA 27.600052 CO MT
6 REDENCAO 27.595255 N PA
7 MANAUS 27.582519 N AM
8 SERRA NOVA DOURADA 27.424863 CO MT
9 MANACAPURU 27.210845 N AM

Time Analysis¶

Plot all variables along the year¶

Para facilitar comparações, criei funções em que é possível exibir parâmetros ao longo do ano para ESTACAO, ESTADO, ou BRASIL, ou inclusive, apresentá-los em pares, lado a lado (ESTACAO + ESTADO, ou ESTACAO + PAIS, ou ESTADO + PAIS)

In [27]:
UFs = ["AC","AL","AP","AM","BA","CE","DF","ES","GO","MA","MT","MS","MG","PA","PB","PR","PE","PI","RJ","RN","RS","RO","RR","SC","SP","SE","TO"]
def prepare_df (*args):
    presentation_df = pd.DataFrame()
    if (len(args)==1):
        target=args[0]
        if target == "BRASIL":
            # presentation_df = tudo
            #temp_df = pd.merge(station_obs.reset_index(), station_params, how='left', on='CODIGO (WMO)').set_index('Data')
            #grouped_df = temp_df.groupby([temp_df.index])[["TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean"]].agg("mean").reset_index().set_index('Data')
            presentation_df = station_obs.groupby([station_obs.index])[list(wheather_mean_parameters.values())].agg("mean").reset_index().set_index('Data') 
        elif target in UFs:
            # adicionar estado em presentation_df
            temp_df = pd.merge(station_obs.reset_index(), station_params.loc[station_params['UF'] == target], how='inner', on='CODIGO (WMO)').set_index('Data')
            grouped_df = temp_df.groupby([temp_df.index,"UF"])[list(wheather_mean_parameters.values())].agg("mean").reset_index().set_index('Data')           
            presentation_df = pd.concat([presentation_df,grouped_df])
        else:
            # adicionar estação em presentation_df
            temp_df = pd.merge(station_obs.reset_index(), station_params.loc[station_params['ESTACAO'] == target], how='inner', on='CODIGO (WMO)').set_index('Data')
            presentation_df = pd.concat([presentation_df,temp_df])
    else:
        target_1 = args[0]
        target_2 = args[1]
        if target_1 == "BRASIL" or target_2 == "BRASIL" :
            # presentation_df = tudo
            #temp_df = pd.merge(station_obs.reset_index(), station_params, how='left', on='CODIGO (WMO)').set_index('Data')
            #grouped_df = temp_df.groupby([temp_df.index])[["TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean"]].agg("mean").reset_index().set_index('Data')
            presentation_df = station_obs.groupby([station_obs.index])[list(wheather_mean_parameters.values())].agg("mean").reset_index().set_index('Data') 
        elif target_1 in UFs and target_2 in UFs:
            # adicionar estado em presentation_df
            temp1_df = pd.merge(station_obs.reset_index(), station_params.loc[station_params['UF'] == target_1], how='inner', on='CODIGO (WMO)').set_index('Data')
            grouped1_df = temp1_df.groupby([temp1_df.index,"UF"])[list(wheather_mean_parameters.values())].agg("mean").reset_index().set_index('Data')           
            temp2_df = pd.merge(station_obs.reset_index(), station_params.loc[station_params['UF'] == target_2], how='inner', on='CODIGO (WMO)').set_index('Data')
            grouped2_df = temp2_df.groupby([temp2_df.index,"UF"])[list(wheather_mean_parameters.values())].agg("mean").reset_index().set_index('Data')           
            presentation_df = pd.concat([grouped1_df,grouped2_df])
        elif ((not target_1 in UFs) and (not target_2 in UFs)):
            # adicionar estação em presentation_df
            temp1_df = pd.merge(station_obs.reset_index(), station_params.loc[station_params['ESTACAO'] == target_1], how='inner', on='CODIGO (WMO)').set_index('Data')
            temp2_df = pd.merge(station_obs.reset_index(), station_params.loc[station_params['ESTACAO'] == target_2], how='inner', on='CODIGO (WMO)').set_index('Data')
            presentation_df = pd.concat([temp1_df,temp2_df])
        else:
            presentation_df = pd.merge(station_obs.reset_index(), station_params.loc[station_params['UF'] == target_1], how='inner', on='CODIGO (WMO)').set_index('Data')
        
    return presentation_df
In [28]:
import plotly.express as px
from plotly.subplots import make_subplots
import plotly.graph_objects as go
from datetime import datetime

def plot_panel(*args):
    df = args[0]
    if (len(args) == 3):
        df = args[0]
        target_1 = args[1]
        target_2 = args[2]
        
        # Split df into df_1 and df_2 according the targets
        if target_1 == "BRASIL":
            df1 = df.copy()
        else:
            if target_1 in UFs and target_2 in UFs:
                df1 = df.loc[df["UF"] == target_1]
            elif ((target_1 in UFs) and (not target_2 in UFs)):
                df1 = df.loc[df["UF"] == target_1].groupby([df.index,"UF"])[list(wheather_mean_parameters.values())].agg("mean").reset_index().set_index('Data')
            else:
                df1 = df.loc[df["ESTACAO"] == target_1]

        if target_2 == "BRASIL":
            df2 = df.copy()
        else:
            if target_2 in UFs:
                df2 = df.loc[df["UF"] == target_2]
            else:
                df2 = df.loc[df["ESTACAO"] == target_2]
        
        l = [key for key in wheather_mean_parameters.keys()]
        fl = []
        for i in l:
            fl.append(i)
            fl.append(i)
        
        # Display both sets
        fig = make_subplots(rows=6, cols=2,shared_xaxes=True,vertical_spacing=0.03,
                            subplot_titles=(fl)
                           )

        # Set 1
        for idx, param in enumerate(wheather_mean_parameters):
            fig.append_trace(
                go.Scatter(x=[datetime.strptime(date, "%Y/%m/%d").date().strftime("%m/%d") for date in df1.index], 
                           y=df1[wheather_mean_parameters[param]]),
                row=idx+1, col=1
            )
            
        # Set 2
        for idx, param in enumerate(wheather_mean_parameters):
            fig.append_trace(
                go.Scatter(x=[datetime.strptime(date, "%Y/%m/%d").date().strftime("%m/%d") for date in df2.index], 
                           y=df2[wheather_mean_parameters[param]]),
                row=idx+1, col=2
            )            
            
        chart_label = "Clima em " + target_1 + " e " + target_2 + " (valores médios) de " + str(df1.index.min()) + " a " + str(df1.index.max())
        fig.update_layout(
            height=800, width=900, title_text=chart_label,showlegend=False
        )
        fig.update_annotations(font_size=10)
        fig.show()
        
    elif (len(args) == 2):
        df = args[0]
        
        fig = make_subplots(rows=6, cols=1,shared_xaxes=True,vertical_spacing=0.03,
                            subplot_titles=([key for key in wheather_mean_parameters.keys()]))

        for idx, param in enumerate(wheather_mean_parameters):
            fig.append_trace(
                go.Scatter(x=[datetime.strptime(date, "%Y/%m/%d").date().strftime("%m/%d") for date in df.index], 
                           y=df[wheather_mean_parameters[param]]),
                row=idx+1, col=1
            )
                
        chart_label = "Clima em " + args[1] + " (valores médios) de " + str(df.index.min()) + " a " + str(df.index.max())
        fig.update_layout(
            height=800, width=980, title_text=chart_label,showlegend=False
        )
        fig.update_annotations(font_size=10)
        fig.show()
In [29]:
def plot_variables(*args):
    if len(args) == 1:
        df = prepare_df(args[0])
        plot_panel(df,args[0])
    elif len(args) == 2:
        df = prepare_df(args[0],args[1])
        plot_panel(df,args[0],args[1])
    else:
        print("Não suportado")

Observando as médias nacionais

In [30]:
plot_variables("BRASIL")
In [31]:
plot_variables("RJ")
In [32]:
plot_variables("SAO PAULO - MIRANTE")

Comparando 2 Estados

In [33]:
plot_variables("SP","RJ")
In [34]:
plot_variables("SP","SC")
In [35]:
plot_variables("SP","SAO PAULO - MIRANTE")
#plot_variables("SAO JOAQUIM","SAO PAULO - MIRANTE")

Itatiaia National Park reached very negative temperatures at the beginning of winter 2022, allowing the formation of two very rare meteorological phenomena. (images: Fluminense Climate Monitoring)

In [36]:
plot_variables("RJ","RESENDE")

Não observamos tal comportamento acima

We remember the tragic episode between the end of March and the beginning of April, when historical accumulations of 500 to 800 mm were added in just 5 days on the north coast of São Paulo and in the south of Rio de Janeiro, where, in Angra dos Reis, at least 15 people died.

In [37]:
plot_variables("BERTIOGA","ANGRA DOS REIS")

In order to verify this statement, we'll need to reload the original file, filtering ANGRA DOS REIS and BERTIOGA that are the target stations and aggregate by their sum

In [38]:
angra_file = data_dir + "/INMET_SE_RJ_A628_ANGRA DOS REIS_01-01-2022_A_31-12-2022.csv"
angra_df = pd.read_csv(
        angra_file,
        encoding=inmet_encoding,
        delimiter=inmet_delimiter,
        decimal=",",
        skiprows=8)
bertioga_file = data_dir + "/INMET_SE_RJ_A628_ANGRA DOS REIS_01-01-2022_A_31-12-2022.csv"
bertioga_df = pd.read_csv(
        bertioga_file,
        encoding=inmet_encoding,
        delimiter=inmet_delimiter,
        decimal=",",
        skiprows=8)

# Observations
angra_df = angra_df.groupby(by="Data")[["PRECIPITAÇÃO TOTAL, HORÁRIO (mm)"]].agg(['sum'])
angra_df = angra_df.loc[(angra_df.index>="2022/03/31") & (angra_df.index<="2022/04/06")]
angra_df[('PRECIPITAÇÃO TOTAL, HORÁRIO (mm)', 'sum')].sum()

bertioga_df = bertioga_df.groupby(by="Data")[["PRECIPITAÇÃO TOTAL, HORÁRIO (mm)"]].agg(['sum'])
bertioga_df = bertioga_df.loc[(bertioga_df.index>="2022/03/31") & (bertioga_df.index<="2022/04/06")]
bertioga_df[('PRECIPITAÇÃO TOTAL, HORÁRIO (mm)', 'sum')].sum()

angra_bert_df = pd.concat([angra_df,bertioga_df])
In [39]:
angra_bert_df.sum()
Out[39]:
PRECIPITAÇÃO TOTAL, HORÁRIO (mm)  sum    680.8
dtype: float64

Pesquisadores investigam causas da tragédia que deixou 133 mortos após fortes chuvas em 2022 no Grande Recife¶

Roberto Quental Coutinho, professor titular do departamento, afirma que, além das causas para os deslizamentos, o projeto analisa, também, os alagamentos e a quantidade de chuva que aconteceu em maio de 2022.

https://g1.globo.com/pe/pernambuco/noticia/2023/06/01/pesquisadores-investigam-causas-da-tragedia-que-deixou-133-mortos-apos-fortes-chuvas-em-2022-no-grande-recife.ghtml

Pernambuco state, mainly Recife, has registered some severe rains. Let´s check

In [40]:
plot_variables("PE")

State averages show an increase level of humidity starting May, which corroborates the report. The most damaged area was Recife. Let´s see

In [41]:
plot_variables("RECIFE")

The RECIFE station (which is in Camaragibe, the most affected city) produce no records in 2022.

REGIAO:;NE
UF:;PE
ESTACAO:;RECIFE
CODIGO (WMO):;A301
LATITUDE:;-8,05916666
LONGITUDE:;-34,95916666
ALTITUDE:;11,3
DATA DE FUNDACAO:;22/12/04
Data;Hora UTC;PRECIPITAÇÃO TOTAL, HORÁRIO (mm);PRESSAO ATMOSFERICA AO NIVEL DA ESTACAO, HORARIA (mB);PRESSÃO ATMOSFERICA MAX.NA HORA ANT. (AUT) (mB);PRESSÃO ATMOSFERICA MIN. NA HORA ANT. (AUT) (mB);RADIACAO GLOBAL (Kj/m²);TEMPERATURA DO AR - BULBO SECO, HORARIA (°C);TEMPERATURA DO PONTO DE ORVALHO (°C);TEMPERATURA MÁXIMA NA HORA ANT. (AUT) (°C);TEMPERATURA MÍNIMA NA HORA ANT. (AUT) (°C);TEMPERATURA ORVALHO MAX. NA HORA ANT. (AUT) (°C);TEMPERATURA ORVALHO MIN. NA HORA ANT. (AUT) (°C);UMIDADE REL. MAX. NA HORA ANT. (AUT) (%);UMIDADE REL. MIN. NA HORA ANT. (AUT) (%);UMIDADE RELATIVA DO AR, HORARIA (%);VENTO, DIREÇÃO HORARIA (gr) (° (gr));VENTO, RAJADA MAXIMA (m/s);VENTO, VELOCIDADE HORARIA (m/s);
2022/01/01;0000 UTC;;;;;;;;;;;;;;;;;;
2022/01/01;0100 UTC;;;;;;;;;;;;;;;;;;
2022/01/01;0200 UTC;;;;;;;;;;;;;;;;;;

etc...

Cyclones in SC¶

https://g1.globo.com/sc/santa-catarina/noticia/2022/11/14/ciclone-extratropical-favorece-temporais-em-sc-na-manha-desta-segunda-feira-alerta-defesa-civil.ghtml

Ciclone extratropical favorece temporais em SC na manhã desta segunda-feira, alerta Defesa Civil
Risco é maior no Oeste. Pode ocorrer chuva intensa, raios, granizo e ventos que podem passar dos 70 km/h.

Nov 14th

https://g1.globo.com/sc/santa-catarina/noticia/2022/09/22/formacao-de-ciclone-deixa-sc-em-alerta-para-rajadas-de-vento-de-ate-100-kmh.ghtml

Formação de ciclone deixa SC em alerta para rajadas de vento de até 100 km/h. 
Ventos devem ser mais fortes no mar, de acordo com a Defesa Civil.

Sep 22nd
In [42]:
plot_variables("SC","SP")
In [43]:
plot_variables("Laguna  - Farol de Santa Marta")
In [44]:
ms_to_kmh = lambda ms : ms/1000*3600
In [45]:
ms_to_kmh(15)
Out[45]:
54.0

Vamos olhar para os dados originais, nestes locais, onde podemos retomar variáveis além das médias

In [46]:
laguna_file = data_dir + "/INMET_S_SC_A866_Laguna  - Farol de Santa Marta_01-01-2022_A_31-12-2022.csv"
laguna_df = pd.read_csv(
        laguna_file,
        encoding=inmet_encoding,
        delimiter=inmet_delimiter,
        decimal=",",
        skiprows=8)
laguna_df['ESTACAO'] = "Laguna"

urussanga_file = data_dir + "/INMET_S_SC_A814_URUSSANGA_01-01-2022_A_31-12-2022.csv"
urussanga_df = pd.read_csv(
        urussanga_file,
        encoding=inmet_encoding,
        delimiter=inmet_delimiter,
        decimal=",",
        skiprows=8)
urussanga_df['ESTACAO'] = "Urussanga"

ararangua_file = data_dir + "/INMET_S_SC_A867_ARARANGUA_01-01-2022_A_31-12-2022.csv"
ararangua_df = pd.read_csv(
        ararangua_file,
        encoding=inmet_encoding,
        delimiter=inmet_delimiter,
        decimal=",",
        skiprows=8)
ararangua_df['ESTACAO'] = "Ararangua"

# Observations
#laguna_df = laguna_df.groupby(by="Data")[["VENTO, RAJADA MAXIMA (m/s)"]].agg(['max'])
#laguna_df = laguna_df.loc[(laguna_df.index>="2022/03/31") & (laguna_df.index<="2022/04/06")]
#laguna_df[('VENTO, RAJADA MAXIMA (m/s)', 'max')].sum()

#urussanga_df = urussanga_df.groupby(by="Data")[["VENTO, RAJADA MAXIMA (m/s)"]].agg(['sum'])
#urussanga_df = urussanga_df.loc[(urussanga_df.index>="2022/03/31") & (urussanga_df.index<="2022/04/06")]
#urussanga_df[('VENTO, RAJADA MAXIMA (m/s)', 'sum')].sum()

#ararangua_df = ararangua_df.groupby(by="Data")[["VENTO, RAJADA MAXIMA (m/s)"]].agg(['sum'])
#ararangua_df = ararangua_df.loc[(ararangua_df.index>="2022/03/31") & (ararangua_df.index<="2022/04/06")]
#ararangua_df[('VENTO, RAJADA MAXIMA (m/s)', 'sum')].sum()

sc_severe_winds_df = pd.concat([laguna_df,urussanga_df,ararangua_df])
In [47]:
sc_severe_winds_df.sort_values(['VENTO, RAJADA MAXIMA (m/s)'],ascending=False).head(10)
Out[47]:
Data Hora UTC PRECIPITAÇÃO TOTAL, HORÁRIO (mm) PRESSAO ATMOSFERICA AO NIVEL DA ESTACAO, HORARIA (mB) PRESSÃO ATMOSFERICA MAX.NA HORA ANT. (AUT) (mB) PRESSÃO ATMOSFERICA MIN. NA HORA ANT. (AUT) (mB) RADIACAO GLOBAL (Kj/m²) TEMPERATURA DO AR - BULBO SECO, HORARIA (°C) TEMPERATURA DO PONTO DE ORVALHO (°C) TEMPERATURA MÁXIMA NA HORA ANT. (AUT) (°C) ... TEMPERATURA ORVALHO MAX. NA HORA ANT. (AUT) (°C) TEMPERATURA ORVALHO MIN. NA HORA ANT. (AUT) (°C) UMIDADE REL. MAX. NA HORA ANT. (AUT) (%) UMIDADE REL. MIN. NA HORA ANT. (AUT) (%) UMIDADE RELATIVA DO AR, HORARIA (%) VENTO, DIREÇÃO HORARIA (gr) (° (gr)) VENTO, RAJADA MAXIMA (m/s) VENTO, VELOCIDADE HORARIA (m/s) Unnamed: 19 ESTACAO
5514 2022/08/18 1800 UTC 1.0 1007.3 1007.5 1006.3 227.6 13.9 11.2 14.5 ... 11.7 9.5 86.0 73.0 84.0 208.0 28.3 17.6 NaN Laguna
5515 2022/08/18 1900 UTC 0.2 1008.6 1008.8 1006.9 185.9 13.6 11.2 14.2 ... 11.2 10.0 86.0 78.0 86.0 211.0 26.4 18.3 NaN Laguna
5451 2022/08/16 0300 UTC 0.0 999.7 999.8 996.9 0.7 20.8 16.2 25.0 ... 17.2 16.1 75.0 61.0 75.0 253.0 25.7 12.4 NaN Laguna
5452 2022/08/16 0400 UTC 0.0 1002.5 1003.0 999.8 0.0 19.4 17.1 20.8 ... 17.4 16.2 88.0 75.0 86.0 192.0 25.7 14.5 NaN Laguna
8303 2022/12/12 2300 UTC 6.0 999.6 999.6 997.1 6.5 19.6 19.2 23.4 ... 22.3 16.8 98.0 90.0 98.0 212.0 25.5 17.2 NaN Laguna
8304 2022/12/13 0000 UTC 4.2 1001.9 1002.0 999.6 1.0 20.7 19.6 21.6 ... 21.0 19.5 98.0 94.0 94.0 209.0 25.5 11.3 NaN Laguna
5516 2022/08/18 2000 UTC 0.4 1010.2 1010.3 1008.6 138.3 14.2 9.3 14.2 ... 11.2 8.6 85.0 69.0 72.0 214.0 25.0 16.9 NaN Laguna
5495 2022/08/17 2300 UTC 0.0 1001.3 1001.4 1000.5 0.0 19.7 17.3 20.0 ... 17.3 17.1 86.0 84.0 86.0 19.0 24.8 17.0 NaN Laguna
5760 2022/08/29 0000 UTC 0.0 1023.4 1023.5 1022.2 0.0 12.6 5.6 13.1 ... 6.8 5.2 66.0 60.0 63.0 215.0 24.7 17.5 NaN Laguna
6048 2022/09/10 0000 UTC 0.0 1003.6 1005.5 1002.7 0.0 18.6 17.5 20.7 ... 18.2 17.5 93.0 86.0 93.0 198.0 24.3 12.7 NaN Laguna

10 rows × 21 columns

In [48]:
ms_to_kmh(28.3)
Out[48]:
101.88000000000001

Como podemos ver acima, a reportagem foi verificada

"Formação de ciclone deixa SC em alerta para rajadas de vento de até 100 km/h. Ventos devem ser mais fortes no mar, de acordo com a Defesa Civil."

In [49]:
laguna_df.drop(columns="Unnamed: 19",inplace=True)
laguna_df.dropna(inplace=True)
In [50]:
# Set an index with datetime(Data + Hora)
laguna_df['Hora UTC'] = laguna_df['Hora UTC'].str[0:2]
laguna_df['data_hora'] = pd.to_datetime(laguna_df['Data'] + " " + laguna_df['Hora UTC'])
laguna_df = laguna_df.set_index('data_hora')

Maximas, Mínimas e Médias ao longo do ano¶

In [51]:
mmm_variables = {
    "Umidade":"UMIDADE RELATIVA DO AR, HORARIA (%)_",
    "Pressão":"PRESSAO ATMOSFERICA AO NIVEL DA ESTACAO, HORARIA (mB)_",
    "Temperatura":"TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_",
    "Vento":"VENTO, VELOCIDADE HORARIA (m/s)_"
}
In [52]:
def plot_mm(station):
    mmm_df = pd.merge(station_obs.reset_index(), station_params.loc[station_params['ESTACAO'] == station], how='inner', on='CODIGO (WMO)').set_index('Data')   
    fig = make_subplots(rows=4, cols=1,shared_xaxes=True,vertical_spacing=0.03,
                    subplot_titles=([key for key in mmm_variables.keys()]))
    
    x_axis=[datetime.strptime(date, "%Y/%m/%d").date().strftime("%m/%d") for date in mmm_df.index]
    for idx, var in enumerate(mmm_variables):
        mmm_cols = [mmm_variables[var] + type for type in ["mean","min","max"]]
#        var_df = mmm_df[mmm_cols]
        fig.add_scatter(x=x_axis, y=mmm_df[mmm_cols[0]], mode='lines',name='Mean',line_width=1,row=idx+1, col=1)
        fig.add_scatter(x=x_axis, y=mmm_df[mmm_cols[1]], mode='lines',name='Max',line_width=1,row=idx+1, col=1)
        fig.add_scatter(x=x_axis, y=mmm_df[mmm_cols[2]], mode='lines',name="Min",line_width=1,row=idx+1, col=1)

#        fig.add_scatter(x=x_axis, y=mmm_df['UMIDADE RELATIVA DO AR, HORARIA (%)_mean'], mode='lines',name='Mean',line_width=1,row=idx+1, col=1)
#        fig.add_scatter(x=x_axis, y=mmm_df['UMIDADE RELATIVA DO AR, HORARIA (%)_max'], mode='lines',name='Max',line_width=1,row=idx+1, col=1)
#        fig.add_scatter(x=x_axis, y=mmm_df['UMIDADE RELATIVA DO AR, HORARIA (%)_min'], mode='lines',name="Min",line_width=1,row=idx+1, col=1)
        
    chart_label = "Médias, Máximas e Mínimas em " + station
    fig.update_layout(
        height=800, width=980, title_text=chart_label,showlegend=False
    )
    fig.update_annotations(font_size=10)
    fig.show()
In [53]:
plot_mm("BRASILIA")

Em qual estação a variação de temperatura máx-min foi maior?

In [54]:
station_obs['temp_variation'] = station_obs["TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_max"] - station_obs["TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_min"]
highest_max_min = station_obs.sort_values("temp_variation",ascending=False)[[
    "CODIGO (WMO)","temp_variation","TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_max","TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_min"
]]
highest_max_min = pd.merge(highest_max_min.reset_index(), station_params, how='inner', on='CODIGO (WMO)').set_index('Data')   
highest_max_min.head(10)
Out[54]:
CODIGO (WMO) temp_variation TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_max TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_min REGIAO UF ESTACAO LATITUDE LONGITUDE ALTITUDE DATA DE FUNDACAO
Data
2022/09/14 A547 27.4 38.6 11.2 SE MG SAO ROMAO -16.362778 -45.123889 490.29 30/06/07
2022/09/11 A547 26.8 38.6 11.8 SE MG SAO ROMAO -16.362778 -45.123889 490.29 30/06/07
2022/09/10 A547 25.5 36.3 10.8 SE MG SAO ROMAO -16.362778 -45.123889 490.29 30/06/07
2022/07/30 A547 25.3 34.4 9.1 SE MG SAO ROMAO -16.362778 -45.123889 490.29 30/06/07
2022/08/06 A547 25.2 35.9 10.7 SE MG SAO ROMAO -16.362778 -45.123889 490.29 30/06/07
2022/07/29 A547 25.1 33.3 8.2 SE MG SAO ROMAO -16.362778 -45.123889 490.29 30/06/07
2022/09/13 A547 25.0 38.4 13.4 SE MG SAO ROMAO -16.362778 -45.123889 490.29 30/06/07
2022/09/04 A547 24.8 37.1 12.3 SE MG SAO ROMAO -16.362778 -45.123889 490.29 30/06/07
2022/08/08 A547 23.7 37.2 13.5 SE MG SAO ROMAO -16.362778 -45.123889 490.29 30/06/07
2022/09/15 A547 23.7 38.9 15.2 SE MG SAO ROMAO -16.362778 -45.123889 490.29 30/06/07
In [55]:
hmm = highest_max_min.groupby([highest_max_min.index,"ESTACAO"])[["temp_variation","UF"]].agg("max").reset_index().set_index('Data').sort_values("temp_variation",ascending=False)
hmm.head(10)
Out[55]:
ESTACAO temp_variation UF
Data
2022/09/14 SAO ROMAO 27.4 MG
2022/09/11 SAO ROMAO 26.8 MG
2022/08/13 JATAI 26.5 GO
2022/08/12 ROSARIO OESTE 25.9 MT
2022/08/10 FORMOSA DO RIO PRETO 25.9 BA
2022/07/30 AGUAS VERMELHAS 25.9 MG
2022/09/10 SAO ROMAO 25.5 MG
2022/09/11 ARACUAI 25.4 MG
2022/09/15 CORRENTINA 25.3 BA
2022/07/30 SAO ROMAO 25.3 MG
In [56]:
hmm.tail(10)
Out[56]:
ESTACAO temp_variation UF
Data
2022/06/06 BURITIRAMA 0.0 BA
2022/06/14 EUCLIDES DA CUNHA 0.0 BA
2022/04/18 SALGUEIRO 0.0 PE
2022/02/05 ARCO VERDE 0.0 PE
2022/06/14 BURITIRAMA 0.0 BA
2022/01/28 BERTIOGA 0.0 SP
2022/07/27 ARCO VERDE 0.0 PE
2022/12/23 EUCLIDES DA CUNHA 0.0 BA
2022/07/22 ARCO VERDE 0.0 PE
2022/04/24 MARIANOPOLIS DO TO 0.0 TO
In [57]:
plot_mm("SAO ROMAO")

Note que esta alteração brusca a partir de 6/Julho surge após um período (8 de abril a 6/Julho) sem dados. Valeria a pena excluir a possibilidade de nenhum erro de leitura, ou qquer outro outlier.

In [58]:
plot_mm("JATAI")

Parece haver um padrão nestas cidades de maior variação... Onde ficam?

In [59]:
cidades_maior_variacao = ["SAO ROMAO","JATAI","ROSARIO OESTE","FORMOSA DO RIO PRETO","AGUAS VERMELHAS","ARACUAI","CORRENTINA"]
In [60]:
station_params.loc[station_params['ESTACAO'].isin(cidades_maior_variacao)]
Out[60]:
REGIAO UF ESTACAO CODIGO (WMO) LATITUDE LONGITUDE ALTITUDE DATA DE FUNDACAO
13 CO GO JATAI A016 -17.923622 -51.717467 670.08 23/05/07
96 CO MT ROSARIO OESTE A944 -14.828889 -56.441944 195.00 30/05/19
116 NE BA CORRENTINA A416 -13.332500 -44.617500 551.71 09/11/07
146 NE BA FORMOSA DO RIO PRETO A452 -11.052222 -45.200833 488.00 02/06/16
385 SE MG SAO ROMAO A547 -16.362778 -45.123889 490.29 30/06/07
387 SE MG AGUAS VERMELHAS A549 -15.751536 -41.457787 754.07 09/09/07
403 SE MG ARACUAI A566 -16.848889 -42.035278 308.00 19/05/17
In [61]:
MV_Map = leafmap.Map(center=[-15, -48], zoom=4)
mv_df = station_params.loc[station_params['ESTACAO'].isin(cidades_maior_variacao)].reset_index()
MV_Map.add_points_from_xy(
    mv_df,
    x="LONGITUDE",
    y="LATITUDE",
    #color_column='region',
    icon_names=['gear', 'map', 'leaf', 'globe'],
    spin=True,
    add_legend=True
)
MV_Map
Map(center=[-15, -48], controls=(ZoomControl(options=['position', 'zoom_in_text', 'zoom_in_title', 'zoom_out_t…

Polígono das Secas¶

In [62]:
import geopandas as gpd
from shapely.geometry import Point, Polygon
from folium import Choropleth, Circle, Marker
from folium.plugins import HeatMap, MarkerCluster
import math
In [63]:
ps_border = ["JEREMOABO","CURACA","SAO JOAO DO PIAUI","CANTO DO BURITI","ALVORADA DO GURGUEIA","BALSAS","DIANOPOLIS","POSSE","CHAPADA GAUCHA","MONTALVANIA","VITORIA DA CONQUISTA","ITIRUCU","ITABERABA","FEIRA DE SANTANA","RIBEIRA DO AMPARO"]
temp_df = station_params.loc[station_params['ESTACAO'].isin(ps_border)]
ps_df = pd.DataFrame()
for st in ps_border:
    row = temp_df.loc[temp_df["ESTACAO"] == st]
    ps_df = pd.concat([ps_df,row])
ps_df
Out[63]:
REGIAO UF ESTACAO CODIGO (WMO) LATITUDE LONGITUDE ALTITUDE DATA DE FUNDACAO
145 NE BA JEREMOABO A450 -10.080833 -38.345833 261.00 13/08/15
143 NE BA CURACA A448 -9.001389 -39.912500 370.00 20/08/15
210 NE PI SAO JOAO DO PIAUI A331 -8.364444 -42.252500 231.00 26/08/07
221 NE PI CANTO DO BURITI A365 -8.118056 -42.975833 312.07 12/06/10
212 NE PI ALVORADA DO GURGUEIA A336 -8.441667 -43.865556 261.26 17/11/07
167 NE MA BALSAS A204 -7.455556 -46.027500 271.03 26/10/07
315 N TO DIANOPOLIS A038 -11.594444 -46.847222 727.87 30/08/08
14 CO GO POSSE A017 -14.089167 -46.366389 830.00 18/04/07
386 SE MG CHAPADA GAUCHA A548 -15.300278 -45.617500 873.20 22/06/07
364 SE MG MONTALVANIA A526 -14.408333 -44.404167 519.52 26/06/07
114 NE BA VITORIA DA CONQUISTA A414 -14.886389 -40.801389 879.38 01/06/07
109 NE BA ITIRUCU A407 -13.527828 -40.119752 757.42 09/02/03
110 NE BA ITABERABA A408 -12.524167 -40.299722 250.11 29/01/03
113 NE BA FEIRA DE SANTANA A413 -12.196111 -38.967500 229.64 26/05/07
149 NE BA RIBEIRA DO AMPARO A458 -11.058611 -38.444167 182.00 20/09/18
In [64]:
ps_codes = ps_df['CODIGO (WMO)'].values
ps_codes
Out[64]:
array(['A450', 'A448', 'A331', 'A365', 'A336', 'A204', 'A038', 'A017',
       'A548', 'A526', 'A414', 'A407', 'A408', 'A413', 'A458'],
      dtype=object)
In [65]:
ps_data = station_obs.loc[station_obs['CODIGO (WMO)'].isin(ps_codes)]
ps_max_temps = ps_data.groupby("CODIGO (WMO)")[["TEMPERATURA MÁXIMA NA HORA ANT. (AUT) (°C)_max"]].agg("max")
In [66]:
ps_max_temps
Out[66]:
TEMPERATURA MÁXIMA NA HORA ANT. (AUT) (°C)_max
CODIGO (WMO)
A017 36.3
A038 36.0
A204 39.1
A331 39.1
A365 38.6
A407 33.0
A413 36.5
A414 33.4
A450 36.4
A458 37.7
A526 34.5
A548 36.2
In [67]:
lats = ps_df['LATITUDE'].values
longs = ps_df['LONGITUDE'].values
stations = ps_df['CODIGO (WMO)'].values
ps_points = [ Point(lats[i],longs[i]) for i in range(0,len(lats))]
In [68]:
ps_polygon = Polygon(zip(longs, lats))
In [69]:
ps_gpd = gpd.GeoDataFrame(index=[0], crs='epsg:4326', geometry=[ps_polygon])       
ps_gpd
Out[69]:
geometry
0 POLYGON ((-38.34583 -10.08083, -39.91250 -9.00...
In [70]:
tm = ps_max_temps['TEMPERATURA MÁXIMA NA HORA ANT. (AUT) (°C)_max'].mean()
t_df = pd.DataFrame(columns=['CODIGO (WMO)','TEMPERATURA MÁXIMA NA HORA ANT. (AUT) (°C)_max'])
for index, row in ps_df.iterrows():
    row_df = pd.DataFrame([[row['CODIGO (WMO)'], tm]],columns=['CODIGO (WMO)','TEMPERATURA MÁXIMA NA HORA ANT. (AUT) (°C)_max'])
    t_df = pd.concat([t_df,row_df])
t_df.set_index("CODIGO (WMO)",drop=True,inplace=True)
prompt_df = pd.merge(t_df, ps_max_temps,how='outer',left_index=True, right_index=True)
prompt_df
Out[70]:
TEMPERATURA MÁXIMA NA HORA ANT. (AUT) (°C)_max_x TEMPERATURA MÁXIMA NA HORA ANT. (AUT) (°C)_max_y
CODIGO (WMO)
A017 36.4 36.3
A038 36.4 36.0
A204 36.4 39.1
A331 36.4 39.1
A336 36.4 NaN
A365 36.4 38.6
A407 36.4 33.0
A408 36.4 NaN
A413 36.4 36.5
A414 36.4 33.4
A448 36.4 NaN
A450 36.4 36.4
A458 36.4 37.7
A526 36.4 34.5
A548 36.4 36.2
In [71]:
m_states = folium.Map(location=(2, -40), zoom_start=4, tiles="cartodb positron")

folium.Choropleth(
    geo_data="../data/Brazil.json",
    data=code_df,
    columns=["UF", "TEMPERATURA DO AR - BULBO SECO, HORARIA (°C)_mean"],
    key_on="feature.properties.UF",
    fill_color="RdYlGn_r",
    fill_opacity=0.8,
    line_opacity=0.3,
    nan_fill_color="white",
).add_to(m_states)

# Mark the locations of PS
for index, row in ps_df.iterrows():
    popup_text = row['ESTACAO'] + ": " + str(t_df.loc[row['CODIGO (WMO)']][0])
    folium.Marker(
        location=[row['LATITUDE'], row['LONGITUDE']],
        popup=popup_text,
        icon=folium.Icon(color="red"),
    ).add_to(m_states)

    # The PS Polygon
sim_geo = gpd.GeoSeries(ps_gpd["geometry"]).simplify(tolerance=0.001)
geo_j = sim_geo.to_json()
geo_j = folium.GeoJson(data=geo_j, style_function=lambda x: {"fillColor": "orange"})
geo_j.add_to(m_states)

# Again, the stations with highest vairance in temperatures
for index, row in mv_df.iterrows():
    folium.Marker(
        location=[row['LATITUDE'], row['LONGITUDE']],
        popup=row['ESTACAO'],
        icon=folium.Icon(color="blue"),
    ).add_to(m_states)
    
m_states
Out[71]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Acima, uma versão simplificada do Polígono das Secas com ALGUMAS cidades em vermelho que mais se aproximam do que se convencio nou.

Em azul, eu reapresentei as cidades com maiores variações de temperaturas diárias.

Conclusão¶

A despeito das técnicas de programação usadas, o dataset é bem interessante para a estudo e prática de tratamento e apresentação de dados

Algumas sugestões de enriquecimento incluem análise de Correlação, inclusive com ALTITUDE, uma variável não observada aqui.

In [ ]: